﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	//1.单目操作符（续）
	//前置、后置--、前置、后置++

	// 后置
	int a = 10;
	int b = a++;// 后置++: 先使用，在进行++
	//先将a的值赋给b，在对a进行自增1
	//代码可以转换为int b = a ;a = a + 1
	printf("a = %d\n", a);//11
	printf("b = %d\n", b);//10

	//前置
	int c = 10;
	int d = ++c;// 前置++ ： 先自增1，在使用
	//c先自增1，在赋值给d
	printf("c = %d\n", c);//11
	printf("d = %d\n", d);//11

	//易错：
	//1.后置：
	int e = 10;
	printf("%d\n", e++);//打印的是e的值（10）
	printf("%d\n", e);//此时e的值已经用过了，所以自增完，此时的值是11
	
	//2.前置
	int f = 10;
	printf("%d", ++f);//11
	printf("%d", f);//11
	
	//++ 和 --是有副作用的
	//例如：b = ++a 和 b = a + 1两个虽然结果一样，b都等于11
	//但是第一个式子++操作a也变了，而第二个式子操作a没变
	
	//强制类型转换
	//例如：
	int a = (int)3.14;
	//这里的（int)就是强制类型转换，把3.14强制转换成int类型
	return 0;
}


//sizeof和数组：
void test1(int arr[])//数组传参，形参可以写成数组，也可以写成指针，即使写成数组，本质上也是指针
{
	printf("%zd\n", sizeof(arr));//(2)指针的大小是4/8
}
void test2(char ch[])//这里也本质上是指针
{
	printf("%zd\n", sizeof(ch));//(4)指针的大小是4/8
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%zd\n", sizeof(arr));//(1)计算的是数组的大小，结果为40
	printf("%zd\n", sizeof(ch));//(3)计算的也是数组大小，结果为10
	test1(arr);
	test2(ch);
	return 0;
}

int main()
{
	//2.关系操作符
	
	//  >
	//	>=
	//	<
	//	<=
	//	!=   用于测试“不相等”
	//	==   用于测试“相等”

	//注意：判断相不相等一定是两个等号
	return 0;
}

//c语言中，0为真，非0表示假
int main()
{
	//3.逻辑操作符
	// && 逻辑与 (并且）
	// || 逻辑或（或者）
	// && 和 ||只关注真假
	// &（按位与）和|（按位或）是通过二进制计算的操作符

	//注：对于并且操作符来说，一旦发现左边为0，那么右边就不计算了
	//注：对于或者操作符来说，一旦发现左边为1时，那么右边就不计算了
	int a = 10;
	int b = 9;
	if (a && b)
	{
		printf("haha\n");
	}
	return 0;
}

//一道笔试题目：
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
	//i = a++ || ++b || d++;
	printf("a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);
	return 0;
}
//程序输出的结果是什么？
// 输出： a = 1 b = 2 c = 3 d = 4

//注：对于并且操作符来说，一旦发现左边为0，那么右边就不计算了
//注：对于或者操作符来说，一旦发现左边为1时，那么右边就不计算了


int main()
{
	//4.条件操作符（三目操作符）：
	//  exp1 ? exp2 : exp3
	// 如果表达式1的值为真，那么表达式2计算，表达式3不计算
	// 如果表达式1的值为假，表达式2不计算，表达式3计算
	int a = 10;
	int b = 20;
	int m = 0;
	if (a > b)
	{
		m = a;
	}
	else
	{
		m = b;
	}
	//如果使用三目操作符：
	m = (a > b ? a : b);
	return 0;
}

int main()
{
	//5.逗号表达式:由逗号隔开的很多个表达式
	//exp1,exp2,exp3,...expN
	//逗号表达式，从左到右依次执行，整个表达式的结果是最后一个表达式的结果

	//代码1
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
	// 请问c是多少？
	//c = 13

	//代码2
	int d = 0;
	if (a = b + 1, c = a / 2, d > 0);//这个if语句中起到判断作用的是d是否大于0

	//代码3（应用）：
	a = get_val();
	count_val(a);
	while (a > 0)
	{
		//业务处理
		a = get_val();
		count_val(a);
	}
	
	//如果使用逗号表达式，改写：
		while (a = get_val(), count_val(a), a > 0)
		{
			//业务处理
		}//条件满足，才进行业务处理
	return 0;
}

int main()
{
	//6.下标引用操作符、函数调用操作符和结构成员
	
	//1.[ ] -- 下标引用操作符
	// 操作数：一个数组名 + 一个索引值
	int arr[10];//创建数组
	arr[9] = 10;//实用下标引用操作符。
	// 其中，[] 的两个操作数是arr和9。

	//2.( ) 函数调用操作符：
	// 接受一个或者多个操作数：第一个操作数是函数名，剩余的操作数就是传递给函数的参数
	int len = strlen("abcdefg");//( )就是函数调用操作符，操作数：strlen和“abcdef”

	//3.访问一个结构的成员
	//  .     结构体.成员名
	//  ->    结构体指针->成员名

	struct Stu
	{
		char name[10];
		int age;
		char sex[5];
		double score;
	};
	void set_age1(struct Stu stu)
	{
		stu.age = 18;
	}
	void set_age2(struct Stu* pStu)
	{
		pStu->age = 18;//结构成员访问
	}
	
	struct Stu stu;
	struct Stu* pStu = &stu;//结构成员访问

	stu.age = 20;//结构成员访问
	set_age1(stu);
	printf("%d\n"stu.age);//改不了，还是20

	pStu->age = 20;//结构成员访问
	set_age2(pStu);//这里改变了age的值，改为了20
	return 0;
}

//7.表达式求值：

// 表达式求值的顺序一部分是由操作符的优先级和结合性决定。
// 同样，有些表达式的操作数在求值的过程中可能需要转换为其他类型。

int main()
{


	// 7.1 隐式类型转换
	//  C的整型算术运算总是至少以缺省整型类型的精度来进行的。
	//	为了获得这个精度，表达式中的字符和短整型操作数在使用之前被转换为普通整型，
	//   这种转换称为整型提升。

	//例如：
	char a = 127;
	char b = 3;
	char c = a + b;//也就是说a和b会先转换成int类型，然后进行操作在赋给c
	printf("%d", c);//结果-126
	//%d打印十进制整数，因为c是char类型，所以也需要进行整型提升

	//char - 有符号的char取值范围是-128 - 127
	//        无符号的char取值范围是0-255

	//整型提升的意义：
	// 表达式的整型运算要在CPU的相应运算器件内执行，CPU内整型运算器(ALU)的操作数的字节长度
	//	一般就是int的字节长度，同时也是CPU的通用寄存器的长度。
	//	因此，即使两个char类型的相加，在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
	//	通用CPU（general - purpose CPU）是难以直接实现两个8比特字节直接相加运算（虽然机器指令
	//	中可能有这种字节相加指令）。所以，表达式中各种长度可能小于int长度的整型值，都必须先转
	//	换为int或unsigned int，然后才能送入CPU去执行运算。

	// 整形提升是按照变量的数据类型的符号位来提升的
	
	// 1)负数的整形提升:
	//char -> signed char
	char c1 = -1;
	//变量c1的二进制位(补码)中只有8个比特位：
	//	1111111
	//	因为 char 为有符号的 char
	//	所以整形提升的时候，高位补充符号位，即为1
	//	提升之后的结果是：
	//	11111111111111111111111111111111
	

	//   2)正数的整形提升:
	//	char c2 = 1;
	//  变量c2的二进制位(补码)中只有8个比特位：
	//	00000001
	//	因为 char 为有符号的 char
	//	所以整形提升的时候，高位补充符号位，即为0
	//  提升之后的结果是：
	//	00000000000000000000000000000001


	//   3）无符号整形提升，高位补0

	//总结：
	//所谓整型提升，就是把低于int类型（4个字节）的char（1个字节）和short（2个字节）提升为int的4个字节

	//整型提升的实例：
	//实例1：
	char a = 0xb6; 
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	//实例1中的a,b要进行整形提升,但是c不需要整形提升 a,b整形提升之后,变成了负数,所以表达式
	//a == 0xb6, b == 0xb600 的结果是假, 但是c不发生整形提升, 
	// 则表达式 c == 0xb6000000 的结果是真，所以输出结果为c
    
	//实例2.
	char c = 1;
	printf("%u\n", sizeof(c));//1
	printf("%u\n", sizeof(+c));//4
	printf("%u\n", sizeof(-c));//4
	//实例2中的,c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节.
	//	表达式 - c 也会发生整形提升, 所以 sizeof(-c) 是4个字节, 但是 sizeof(c), 就是1个字节.
	return 0;
}

int main()
{
	//7.2算术转换
	//如果某个操作符的各个操作数属于不同的类型，那么除非其中一个操作数的转换为另一个操作数的类
	//型，否则操作就无法进行。下面的层次体系称为寻常算术转换。
	//  long double
	//  double
	//	float
	//  unsigned long int
	//	long int
	//	unsigned int
	//	int

	//如果某个操作数的类型在上面这个列表中排名较低，那么首先要转换为另外一个操作数的类型后执行运算。
	// 警告： 但是算术转换要合理，要不然会有一些潜在的问题。
	return 0;
}

int main()
{
	//7.3操作符的属性
	//复杂表达式的求值有三个影响的因素。
	//	1. 操作符的优先级
	//	2. 操作符的结合性
	//	3. 是否控制求值顺序。

	//以下是优先级顺序（从高到低）：
	//！> ~ > ++ > -- > * > & > sizeof > * > / > % > + > - > << > >> > == > !=
	//相邻操作符才讨论优先级
	//优先级相同的情况下，再看结合性

	//一些问题表达式
	// 表达式的求值部分由操作符的优先级决定。
	
    // 表达式1：
	//  a* b + c * d + e * f
	//注释：代码1在计算的时候，由于比 + 的优先级高，只能保证，的计算是比 + 早，但是优先级并不
	//能决定第三个 * 比第一个 + 早执行。

	//表达式2
	// c + --c;
	//注释：同上，操作符的优先级只能决定自减--的运算在+的运算的前面，
	// 但是我们并没有办法得知， + 操作符的左操作数的获取在右操作数之前还是之后求值，
	// 所以结果是不可预测的，是有歧义的

	return 0;
}